#include <openssl/pem.h>
#include <cstring>
#include "myrsa.h"
#include "macro.h"
#include "log.h"
#include "myhash.h"

static sylar::Logger::ptr g_logger = DANIEL_LOG_NAME("system");

namespace daniel
{
    MyRsa::MyRsa(OperationType type, const std::string &fileName,bool isFile) : m_type(type)
    {
        if (type == LoadPublicKey)
        {
            m_publicKey = RSA_new();
        }
        else if (type == LoadPrivateKey)
        {
            m_privateKey = RSA_new();
        }
        else
        {
            m_publicKey = RSA_new();
            m_totalKey = RSA_new();
            m_privateKey = RSA_new();
        }

        if (type == LoadPublicKey || type == LoadPrivateKey)
        {
            DANIEL_ASSERT2(!fileName.empty(), "fileName can't be empty!");
            loadKeyFromFile(fileName,isFile);
        }
    }
    MyRsa::~MyRsa()
    {
        DANIEL_LOG_DEBUG(g_logger) << "myrsa 析构！";
        if (m_type == GenerateKeys)
        {
            RSA_free(m_totalKey);
            RSA_free(m_publicKey);
            RSA_free(m_privateKey);
        }
        else if (m_type == LoadPublicKey)
        {
            RSA_free(m_publicKey);
        }
        else
        {
            RSA_free(m_privateKey);
        }
    }
    void MyRsa::generateKeys(int bits)
    {
        BIGNUM *e = BN_new();
        BN_set_word(e, 12345);
        // int ret=0;
        // ret =
        int ret = RSA_generate_key_ex(m_totalKey, bits, e, nullptr);
        DANIEL_ASSERT2(ret == 1, "RSA_generate_key_ex failed!");
        // DANIEL_LOG_DEBUG(g_log)<<"ret="<<ret<<std::endl;
        m_publicKey = RSAPublicKey_dup(m_totalKey);
        m_privateKey = RSAPrivateKey_dup(m_totalKey);
        BN_free(e);
    }
    void MyRsa::saveKeysToFile(const std::string &publicKeyFile, const std::string &privateKeyFile)
    {
        BIO *bioPublic = BIO_new_file(publicKeyFile.c_str(), "w");
        BIO *bioPrivate = BIO_new_file(privateKeyFile.c_str(), "w");
        int retPublic = 0, retPrivate = 0;
        retPublic = PEM_write_bio_RSAPublicKey(bioPublic, m_totalKey);
        // DANIEL_LOG_DEBUG(DANIEL_LOG_ROOT())<<"retPublic="<<retPublic;
        DANIEL_ASSERT2(retPublic == 1, "PEM_write_bio_RSAPublicKey failed");
        retPrivate = PEM_write_bio_RSAPrivateKey(bioPrivate, m_totalKey, nullptr, nullptr, 0, nullptr, nullptr);
        DANIEL_ASSERT2(retPrivate == 1, "PEM_write_bio_RSAPublicKey failed");
        //需要增加错误处理，最少需要加一个断言
        BIO_free(bioPublic);
        BIO_free(bioPrivate);
    }
    void MyRsa::loadKeyFromFile(const std::string &fileName, bool isFile)
    { 
        BIO *bio=nullptr;
        if (isFile){
            bio = BIO_new_file(fileName.c_str(), "r");
        }
        else{
            bio = BIO_new_mem_buf((void *)fileName.c_str(), fileName.length());
        }
        RSA *retPtr;
        if (m_type == LoadPublicKey)
        {
            retPtr = PEM_read_bio_RSAPublicKey(bio, &m_publicKey, nullptr, nullptr);
            DANIEL_ASSERT2(retPtr != nullptr, "PEM_read_bio_RSAPublicKey failed!");
        }
        else if (m_type == LoadPrivateKey)
        {
            retPtr = PEM_read_bio_RSAPrivateKey(bio, &m_privateKey, nullptr, nullptr);
            DANIEL_ASSERT2(retPtr != nullptr, "PEM_read_bio_RSAPrivateKey failed!");
        }
        //需要增加错误处理
        BIO_free(bio);
    }

    //以下四个函数都需要加出错处理
    void MyRsa::publicEncrypt(int flen, const unsigned char *from,
                              unsigned char *to, int padding)
    {
        //返回编码之后，字符串长度
        int ret = RSA_public_encrypt(flen, from, to, m_publicKey, padding);
        // DANIEL_LOG_DEBUG(DANIEL_LOG_ROOT())<<"ret="<<ret;
        DANIEL_ASSERT2(ret != 0, "RSA_public_encrypt failed!");
    }
    void MyRsa::publicEncrypt(const std::string &from, std::string &to)
    {
        //公钥加密，并进行base64编码；
        to.clear();
        int keyLen = RSA_size(m_publicKey);
        char buf[keyLen];
        memset(buf, 0, keyLen);
        int ret = ::RSA_public_encrypt(from.length(), (uint8_t *)from.c_str(), (uint8_t *)buf, m_publicKey, RSA_PKCS1_PADDING);
        DANIEL_ASSERT2(ret != -1, "rsa public encrypt failed!");
        std::string tempStr(buf, keyLen);
        to = MyRsa::base64Encode(tempStr);
    }
    void MyRsa::privateEncrypt(int flen, const unsigned char *from,
                               unsigned char *to, int padding)
    {
        int ret = RSA_private_encrypt(flen, from, to, m_privateKey, padding);
        DANIEL_ASSERT2(ret != -1, "RSA_private_encrypt failed!");
    }
    void MyRsa::privateEncrypt(const std::string &from, std::string &to)
    {
        to.clear();
        int keyLen = RSA_size(m_privateKey);
        char buf[keyLen];
        memset(buf, 0, keyLen);
        int ret = ::RSA_private_encrypt(from.length(), (uint8_t *)from.c_str(), (uint8_t *)buf, m_privateKey, RSA_PKCS1_PADDING);
        DANIEL_ASSERT2(ret != -1, "rsa public encrypt failed!");
        std::string tempStr(buf, keyLen);
        to = MyRsa::base64Encode(tempStr);
    }
    //私钥、公钥解密功能
    void MyRsa::publicDecrypt(int flen, const unsigned char *from,
                              unsigned char *to, int padding)
    {
        int ret = RSA_public_decrypt(flen, from, to, m_publicKey, padding);
        DANIEL_ASSERT2(ret != -1, "RSA_public_decrypt failed!");
    }
    void MyRsa::publicDecrypt(const std::string &from, std::string &to)
    {
        to.clear();
        DANIEL_LOG_DEBUG(g_logger) << "from in publicDecrypt:"
                                   << "\n"
                                   << from << std::endl;
        std::string tempStr = MyRsa::base64Decode(from);
        DANIEL_LOG_DEBUG(g_logger) << "Decode base64 in publicDecrypt:"
                                   << "\n"
                                   << tempStr << std::endl;
        int keyLen = RSA_size(m_publicKey);
        char buf[keyLen];
        memset(buf, 0, keyLen);
        int ret = ::RSA_public_decrypt(keyLen, (uint8_t *)tempStr.c_str(), (uint8_t *)buf, m_publicKey, RSA_PKCS1_PADDING);
        DANIEL_LOG_DEBUG(g_logger) << "ret:" << ret;
        DANIEL_ASSERT2(ret != -1, "rsa public decrypt failed!");
        int i = 0;
        while (buf[i] != '\000')
        {
            to.push_back(buf[i]);
            i++;
        }
        DANIEL_LOG_DEBUG(g_logger) << "to:" << to;
    }
    void MyRsa::privateDecrypt(int flen, const unsigned char *from,
                               unsigned char *to, int padding)
    {
        int ret = RSA_private_decrypt(flen, from, to, m_privateKey, padding);
        DANIEL_ASSERT2(ret != -1, "RSA_private_decrypt failed!");
    }
    void MyRsa::privateDecrypt(const std::string &from, std::string &to)
    {
        to.clear();
        DANIEL_LOG_DEBUG(g_logger) << "from string in privateDecrypt:"
                                   << "\n"
                                   << from << std::endl;
        std::string tempStr = MyRsa::base64Decode(from);
        DANIEL_LOG_DEBUG(g_logger) << "Decode base64 in privateDecrypt:"
                                   << "\n"
                                   << tempStr << std::endl;
        int keyLen = RSA_size(m_privateKey);
        char buf[keyLen];
        memset(buf, 0, keyLen);
        int ret = ::RSA_private_decrypt(keyLen, (uint8_t *)tempStr.c_str(), (uint8_t *)buf, m_privateKey, RSA_PKCS1_PADDING);
        DANIEL_LOG_DEBUG(g_logger) << "ret:" << ret;
        DANIEL_ASSERT2(ret != -1, "rsa private decrypt failed!");
        int i = 0;
        while (buf[i] != '\000')
        {
            to.push_back(buf[i]);
            i++;
        }
        // DANIEL_LOG_DEBUG(g_logger)<<"to:"<<to;
    }
    //使用私钥签名  type=NID_MD5, NID_SHA1, NID_SHA224
    //签名的数据长度大于0，并且小于  密钥长度-11   对签名长度有限制
    //如果需要其它长度数据签名，需要自己封装
    bool MyRsa::RSA_sign(SignType type, const unsigned char *m, unsigned int m_length, unsigned char *sigret, unsigned int *siglen)
    {

        int ret = ::RSA_sign(type, m, m_length, sigret, siglen, m_privateKey);
        DANIEL_LOG_DEBUG(g_logger) << "ret=" << ret << ",siglen=" << siglen;
        DANIEL_ASSERT2(ret == 1, "RSA_sign failed!");
        return ret == 1 ? true : false;
    }
    bool MyRsa::RSA_sign(SignType type, const std::string &data, std::string &retData)
    {
        uint32_t len = 0;
        char *signBuf = new char[RSA_size(m_privateKey) + 1];
        if (m_privateKey == nullptr)
        {
            DANIEL_LOG_DEBUG(g_logger) << "m_privateKey is null";
        }
        int ret = ::RSA_sign(type, (unsigned char *)data.data(), data.size(), (unsigned char *)signBuf, &len, m_privateKey);
        DANIEL_ASSERT2(ret == 1, "RSA_sign failed!");
        // DANIEL_LOG_DEBUG(g_logger) << "ret=" << ret << ",siglen=" << len;
        std::string retStr = std::string(signBuf, len);
        retData = retStr;
        DANIEL_LOG_DEBUG(g_logger) << retData;
        delete[] signBuf;
        return true;
    }
    bool MyRsa::RSA_hashsign(SignType type, const std::string &data, std::string &retData)
    {
        MyHash::ptr myhash(new MyHash(MyHash::SHA256));
        myhash->init();
        myhash->update((void *)data.c_str(), data.length());
        myhash->final();
        std::string hashString = myhash->toHashString();
        bool ret = RSA_sign(type, hashString, retData);
        return ret;
    }
    // type和签名保持一致
    bool MyRsa::RSA_verify(SignType type, const unsigned char *m, unsigned int m_length, const unsigned char *sigbuf, unsigned int siglen)
    {
        int ret = ::RSA_verify(type, m, m_length, sigbuf, siglen, m_publicKey);
        // DANIEL_ASSERT2(ret==1,"RSA_verify failed");
        return ret == 1 ? true : false;
    }

    bool MyRsa::RSA_verify(SignType type, const std::string &msgStr, const std::string &sigStr)
    {
        int ret = ::RSA_verify(type, (uint8_t *)(msgStr.c_str()), msgStr.size(), (uint8_t *)sigStr.c_str(), sigStr.size(), m_publicKey);
        return ret == 1 ? true : false;
    }
    bool MyRsa::RSA_sign_extend(SignType type, const std::string &data, std::string &retData)
    {
        MyHash::ptr hash;
        switch (type)
        {
        case SignMD5:
            hash.reset(new MyHash(MyHash::MD5));
            break;
        case SignSHA1:
            hash.reset(new MyHash(MyHash::SHA1));
            break;
        case SignSHA224:
            hash.reset(new MyHash(MyHash::SHA224));
            break;
        case SignSHA256:
            hash.reset(new MyHash(MyHash::SHA256));
            break;
        case SignSHA384:
            hash.reset(new MyHash(MyHash::SHA384));
            break;
        case SignSHA512:
            hash.reset(new MyHash(MyHash::SHA512));
            break;
        default:
            break;
        }
        hash->init();
        hash->update((void *)data.c_str(), data.length());
        hash->final();
        std::string hashString = hash->toHashString();
        int8_t buf[::RSA_size(m_privateKey) + 1];
        memset(buf, 0, ::RSA_size(m_privateKey) + 1);
        //对hash得到的string进行私钥加密，得到签名
        publicEncrypt(::RSA_size(m_privateKey), (uint8_t *)hashString.c_str(), (uint8_t *)buf);
        // retData.assign(buf,::RSA_size(m_privateKey)+1);
        for (int i = 0; i < ::RSA_size(m_privateKey) + 1; i++)
        {
            retData.push_back(buf[i]);
        }
        return !retData.empty();
    }
    bool MyRsa::RSA_verify_extend(SignType type, const std::string &msgStr, const std::string &sigStr)
    {
        // msgStr进行求解hash值，
        MyHash::ptr hash;
        switch (type)
        {
        case SignMD5:
            hash.reset(new MyHash(MyHash::MD5));
            break;
        case SignSHA1:
            hash.reset(new MyHash(MyHash::SHA1));
            break;
        case SignSHA224:
            hash.reset(new MyHash(MyHash::SHA224));
            break;
        case SignSHA256:
            hash.reset(new MyHash(MyHash::SHA256));
            break;
        case SignSHA384:
            hash.reset(new MyHash(MyHash::SHA384));
            break;
        case SignSHA512:
            hash.reset(new MyHash(MyHash::SHA512));
            break;
        default:
            break;
        }
        hash->init();
        hash->update((void *)msgStr.c_str(), msgStr.length());
        hash->final();
        std::string hashString = hash->toHashString();
        //将sigStr进行解密
        // publicDecrypt(m_publicKey,(uint8_t*)sigStr.data(),)
        std::string decryptStr;
        return hashString == decryptStr;
    }
    bool MyRsa::RSA_hashverify(SignType type, const std::string &msgStr, const std::string &sigStr)
    {
        // msgStr  原始数据，hash算法之后，得到hash之后的值；
        //相当于签名之前的数据
        MyHash::ptr myhash(new MyHash(MyHash::SHA256));
        myhash->init();
        myhash->update((void *)msgStr.c_str(), msgStr.length());
        myhash->final();
        std::string hashString = myhash->toHashString();
        // sigStr签名之后的数据，需要解密，然后判断和签名之前的数据是否相等
        bool ret = RSA_verify(type, hashString, sigStr);
        return ret;
    }

    std::string MyRsa::base64Encode(const std::string &msg)
    {
        //构造合适的BIO链，先base64编码，然后将编码之后的内容写入内存
        //再将内存中的内容从内存读入到字符串中
        BIO *base64 = BIO_new(BIO_f_base64());
        BIO *mem = BIO_new(BIO_s_mem());
        BIO_push(base64, mem);
        int ret = BIO_write(base64, (void *)msg.c_str(), msg.length());
        BIO_flush(base64);
        DANIEL_ASSERT2(ret > 0, "BIO_write failed!");
        BUF_MEM *dataPtr;
        BIO_get_mem_ptr(base64, &dataPtr);
        char buf[dataPtr->length];
        memcpy((void *)buf, dataPtr->data, dataPtr->length);
        // printf("buf:%s\n",buf);
        std::string retStr(dataPtr->data, dataPtr->length);
        // retStr.assign();
        // memcpy((void*)buf,(void*)dataPtr->data,dataPtr->length);
        BUF_MEM_free(dataPtr);
        BIO_free(base64);
        return retStr;
    }
    //解码操作
    std::string MyRsa::base64Decode(const std::string &encodeMsg)
    {
        //构造BIO链条
        BIO *base64 = BIO_new(BIO_f_base64());
#if 0
            BIO* mem=BIO_new(BIO_s_mem());
            BIO_write(mem,(void*)encodeMsg.c_str(),encodeMsg.length());
#else
        BIO *mem = BIO_new_mem_buf((void *)encodeMsg.c_str(), encodeMsg.length());
#endif
        BIO_push(base64, mem);
        char buf[4096];
        std::string retStr;
        while (1)
        {
            memset((void *)buf, 0, 4096);
            int32_t ret = BIO_read(base64, (void *)buf, 4096);
            if (ret <= 0)
                break;
            for (int i = 0; i < ret; i++)
            {
                // if (buf[i]=='\0') break;
                retStr.push_back(buf[i]);
            }
        }
        BIO_free(base64);
        return retStr;
    }
}